package com.tuling.vipes.controller;

import com.tuling.vipes.service.ElasticsearchSelectService;
import org.elasticsearch.client.indices.AnalyzeResponse;
import org.elasticsearch.index.query.Operator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * @创建人: Mhh
 * @创建时间: 2021/12/10
 * @描述: 查询类
 */
@RestController
@RequestMapping("/elasticSearch/select")
public class ElasticsearchSelectController {

    @Autowired
    private ElasticsearchSelectService elasticsearchSelectService;

    /**
     * @param id        : 主键id
     * @param classType : 类对象(用来获取这个对象中的@Document注解中indexName属性[表示查询那个索引])
     * @return T
     * @explain : 根据主键id查询数据
     * @Author Mhh
     * @Date 2021/12/10 10:46
     */
    @PostMapping("get")
    public <T> T get(String id, Class<T> classType) {
        return elasticsearchSelectService.get(id, classType);
    }

    /**
     * @param id        : 主键id
     * @param classType : 类对象(用来获取这个对象中的@Document注解中indexName属性[表示查询那个索引])
     * @return java.lang.Boolean
     * @explain : 判断主键id是否存在
     * @Author Mhh
     * @Date 2021/12/29 14:54
     */

    @PostMapping("exists")
    public Boolean exists(String id, Class<?> classType) {
        return elasticsearchSelectService.exists(id, classType);
    }

    /**
     * @param pageNum   : 分页查询的页数
     * @param pageSize  : 分页查询返回的每页个数
     * @param key       : 查询es的字段名
     * @param value     : 要查询字段名中的值
     * @param classType : 返回的类类型
     * @return List<T></T>
     * @throws
     * @Author Mhh
     * @Date 2021/12/9 10:36
     */
    @PostMapping("selectFindPage")
    public <T> List<T> selectFindPage(Integer pageNum, Integer pageSize, String key, String value, Class<T> classType) {
        return elasticsearchSelectService.selectFindPage(pageNum, pageSize, key, value, classType);
    }

    /**
     * @param tokenizer : 粗细粒度分词(粗粒度:ik_smart   细粒度:ik_max_word)
     * @param text      : 需要分词的入参
     * @return java.util.List<org.elasticsearch.client.indices.AnalyzeResponse.AnalyzeToken>
     * @throws
     * @explain
     * @Author Mhh
     * @Date 2021/12/9 16:16
     */
    @PostMapping("selectBreakUpText")
    public List<AnalyzeResponse.AnalyzeToken> selectBreakUpText(String tokenizer, String text) throws NoSuchFieldException, IllegalAccessException, IOException {
        return elasticsearchSelectService.selectBreakUpText(tokenizer, text);
    }

    /**
     * @param key       : es里索引的域(字段名)
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @param values    : 一域多值, 查询的值
     * @return java.util.List<T>
     * @explain :词条查询：不分词，精确匹配
     * @Author Mhh
     * @Date 2021/12/10 14:28
     */
    @PostMapping("termQuery")
    public <T> List<T> termQuery(String key, Class<T> classType, String... values) {
        return elasticsearchSelectService.termQuery(key, classType, values);
    }

    /**
     * 入参分词: 山东省济南市  ik_smart粗粒度:[山东省,济南市] ik_max_word细粒度:[山东省,山东,省,济南市,济南,南市]
     *
     * @param operator  : Operator.OR(并集) [默认] 只要分的词有一个和索引字段上对应上则就返回
     *                  Operator.AND(交集)   分的词全部满足的数据返回
     * @param analyzer  : 选择分词器[ik_smart粗粒度,ik_max_word细粒度] 默认:ik_max_word细粒度
     * @param key       :  es里索引的域(字段名)
     * @param classType :  返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @param text    :  查询的值
     * @return java.util.List<T>
     * @explain ：matchQuery:词条分词查询(会对查询条件进行分词)
     * @Author Mhh
     * @Date 2021/12/10 15:21
     */
    @PostMapping("matchQuery")
    public <T> List<T> matchQuery(Operator operator, String analyzer, String key, Class<T> classType, String text) {
        return elasticsearchSelectService.matchQuery(operator, analyzer, key, classType, text);
    }

    /**
     * @param classType :返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain : 返回索引库里所有数据
     * @Author Mhh
     * @Date 2021/12/10 15:44
     */
    @PostMapping("matchAllQuery")
    public <T> List<T> matchAllQuery(Class<T> classType) {
        return elasticsearchSelectService.matchAllQuery(classType);
    }

    /*
     *   ?:只包含一个字符
     *       山?省:  山东省 或者 山西省 等等   ?包含一个字符
     *       ??省：  山东省 或者 吉林省 等等   ?包含一个字符
     *       ???:    你好啊 或者 早上好 等等 ?包含一个字符
     *
     *   *:表示0个或多个字符
     *       济南*： 济南市 或者 济南市历下区.....  *表示0个或多个字符
     *       *剑 ：  长虹剑 或者 冰魄长虹倚天剑.... *表示0个或多个字符
     *
     * 注意: *或者?放在最前面(例如：*省 | ?省    *为 | ?为) 会引发全表(全索引)扫描  注意效率问题
     * */

    /**
     * @param key       :  es里索引的域(字段名)
     * @param value     : 查询的值
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain :   wildcardQuery模糊查询(会对查询条件分词，还可以使用通配符)[?:表示任意单个字符][*:表示0或多个字符]
     * @Author Mhh
     * @Date 2021/12/10 16:10
     */

    @PostMapping("wildcardQuery")
    public <T> List<T> wildcardQuery(String key, String value, Class<T> classType) {
        return elasticsearchSelectService.wildcardQuery(key, value, classType);
    }

    /**
     * @param key       :  es里索引的域(字段名)
     * @param value     : 查询的值
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain :  prefixQuery 前缀查询 对keyword类型支持比较好(text也能用:索引库字段分词后 分的词前缀要是能匹配也是可以返回此数据)
     * @Author Mhh
     * @Date 2021/12/10 16:49
     */

    @PostMapping("prefixQuery")
    public <T> List<T> prefixQuery(String key, String value, Class<T> classType) {
        return elasticsearchSelectService.prefixQuery(key, value, classType);
    }

    /**
     * @param key       :  es里索引的域(字段名)
     * @param value     : 查询的值
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain : 正则表达式查询:regexpQuery
     * @Author Mhh
     * @Date 2021/12/11 19:17
     */
    @PostMapping("regexpQuery")
    public <T> List<T> regexpQuery(String key, String value, Class<T> classType) {
        return elasticsearchSelectService.regexpQuery(key, value, classType);
    }

    /**
     * @param name      :es里索引的域(字段名)
     * @param from      :范围查询值 1 from(Object from, boolean includeLower) includeLower：是否包含 默认为true[包含]
     *                  //              如果from==null  那么就是<=to的
     * @param to        :范围查询值 2 to(Object to, boolean includeUpper) includeLower：是否包含 默认为true[包含]
     *                  //              如果to==null 那么就是 >=from的
     * @param classType :返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain : rangeQuery 范围查询
     * @Author Mhh
     * @Date 2021/12/12 18:13
     */
    @PostMapping("rangeQuery")
    public <T> List<T> rangeQuery(String name, Integer from, Integer to, Class<T> classType) {
        return elasticsearchSelectService.rangeQuery(name, from, to, classType);
    }

    /**
     * @param direction : 排序 Sort.Direction.ASC:升序  ||  Sort.Direction.DESC:降序
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @param properties       : 域名(可变参数,可传1至多个)
     * @return java.util.List<T>
     * @explain : 给查询 结果排序
     * @Author Mhh
     * @Date 2021/12/12 18:45
     */
    @PostMapping("sort")
    public <T> List<T> sort(Sort.Direction direction, Class<T> classType, String... properties) {
        return elasticsearchSelectService.sort(direction, classType, properties);
    }

    /*
     *      多域查询的交并集理解:
     *                      OR:  只要有一个域中包含入参value被分词后的"一个值"时就返回
     *                      AND: 只要有一个域中包含入参value被分词后的"所有值"时返回
     *
     * @param fields    : Map<String,Float>类型:key为域名,Float为boost值
     *                  boost: 参数被用来提升一个语句的相对权重（ boost 值大于 1 ）或降低相对权重（ boost 值处于 0 到 1 之间），但是这种提升或降低并不是线性的，换句话说，如果一个 boost 值为 2 ，并不能获得两倍的评分 _score 。
     * @param queryString     : 要查询的值 (会对查询条件进行分词)
     * @param analyzer  : 选择分词器[ik_smart粗粒度,ik_max_word细粒度] 默认:ik_max_word细粒度
     * @param operator  : Operator.OR(并集) [默认] 只要分的词有一个和索引字段上对应上则就返回
     *                  Operator.AND(交集)   分的词全部满足的数据返回
     * @param classType :  返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain :  queryString 多条件查询
     * •会对查询条件进行分词。
     * •然后将分词后的查询条件和词条进行等值匹配
     * •默认取并集（OR）
     * •可以指定多个查询字段
     * •query_string：识别query中的连接符（or 、and）
     * @Author Mhh
     * @Date 2021/12/12 19:45
     */

    @PostMapping("queryStringQuery")
    public <T> List<T> queryStringQuery(Map<String, Float> fields, String queryString, String analyzer, Operator operator, Class<T> classType) {
        return elasticsearchSelectService.queryStringQuery(fields, queryString, analyzer, operator, classType);
    }

    /**
     * ------boolQuery子句用termsQuery做的测试
     * //               fields: 只要有一条数据包含fields 都包含value数组中的一种就能返回
     *
     * @param fields    :Map<String,String[]> key:es索引库里的域(字段名), value数组:一域多值, 查询的值
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain : boolQuery：返回的文档必须满足must子句的条件,并且参与计算分值
     * //                   条件必须成立，性能比filter低。会计算得分
     * //                   对多个查询条件连接。连接方式：must（and）：条件必须成立(会计算得分)
     * //                   maxSore(得分):即条件匹配度,匹配度越高，得分越高
     * @Author Mhh
     * @Date 2021/12/13 10:31
     */
    @PostMapping("boolQueryBuilderByMust")
    public <T> List<T> boolQueryBuilderByMust(Map<String, String[]> fields, Class<T> classType) {
        return elasticsearchSelectService.boolQueryBuilderByMust(fields, classType);
    }



    /**
     * ------boolQuery子句用termsQuery做的测试
     * //               fields: 只要有一条数据包含fields 都包含value数组中的一种就能返回
     *
     * @param fields    :Map<String,String[]> key:es索引库里的域(字段名), value数组:一域多值, 查询的值(must:数组中多值是或者的关系，只要是所有字段能对应到数组中至少一个值就可以返回)
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain : boolQuery：返回的文档必须满足filter子句的条件,但是不会像must一样,参与计算分值
     * //                   对多个查询条件连接。连接方式：filter(and)：条件必须成立，性能比must高。不会计算得分
     * //                   maxSore(得分):即条件匹配度,匹配度越高，得分越高
     * @Author Mhh
     * @Date 2021/12/13 10:31
     */
    @PostMapping("boolQueryBuilderByFilter")
    public <T> List<T> boolQueryBuilderByFilter(Map<String, String[]> fields, Class<T> classType) {
        return elasticsearchSelectService.boolQueryBuilderByFilter(fields, classType);
    }


    /**
     * ------boolQuery子句用termsQuery做的测试
     *
     * @param fields    :Map<String,String[]> key:es索引库里的域(字段名), value数组:一域多值, 查询的值(must_not:只要是所有字段都对应不到数组中的值就可以返回)
     *                  //fields 只要数据不包含在 value数组中指定的返回
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain : boolQuery：返回的文档必须不满足定义的条件
     * //           对多个查询条件连接。连接方式：must_not（not）：条件必须不成立
     * @Author Mhh
     * @Date 2021/12/19 18:55
     */

    @PostMapping("boolQueryBuilderByMustNot")
    public <T> List<T> boolQueryBuilderByMustNot(Map<String, String[]> fields, Class<T> classType) {
        return elasticsearchSelectService.boolQueryBuilderByMustNot(fields, classType);
    }


    /**
     * ------boolQuery子句用termsQuery做的测试
     *
     * @param fields             :Map<String,String[]> key:es索引库里的域(字段名), value数组:一域多值, 查询的值(should:只要有一条数据对应的字段包含 value数组的值就返回)
     *                           // 只要有一条数据对应的字段包含 value数组的值就返回
     * @param minimumShouldMatch : 参数定义了至少满足几个子句
     * @param classType          : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain : boolQuery：返回的文档可能满足should子句的条件.
     * //                   在一个bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回
     * @Author Mhh
     * @Date 2021/12/19 19:23
     */
    @PostMapping("boolQueryBuilderByShould")
    public <T> List<T> boolQueryBuilderByShould(Map<String, String[]> fields, Integer minimumShouldMatch, Class<T> classType) {
        return elasticsearchSelectService.boolQueryBuilderByShould(fields, minimumShouldMatch, classType);
    }

    /**
     * 速度快  输入的内容立即返回  对字段类型要求多节省存储空间  时间复杂度O(1)，做建议不做纠错
     * 感觉和prefixQuery 前缀查询 差不多.....
     * <p>
     * 搜索补全必须定义 这个属性(
     * //                  @CompletionField(analyzer = "ik_smart", searchAnalyzer = "ik_smart", maxInputLength = 100)
     * //                  private Completion completion;)
     * //      给Completion属性赋值: new Completion(new String[]{"山东省泰安市岱岳区"}))
     * //                  :里面的值就是被自动补全的值
     *
     * @param fieldName : 要用哪个字段进行标题联想(必须是这个@CompletionField注解所标注的类型为Completion的字段名)
     * @param text      : 被补全的值(比如传的是山东 可能就能给补全为 山东省)
     * @param classType : 返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<java.lang.String>
     * @explain : 搜索补全功能 比如在输入框输入(天上)下面就自动补全 (天上人间)(天上边的彩霞)(....)
     * @Author Mhh
     * @Date 2021/12/22 16:51
     */

    @PostMapping("completionSuggestion")
    public List<String> completionSuggestion(String fieldName, String text, Class<?> classType) {
        return elasticsearchSelectService.completionSuggestion(fieldName, text, classType);
    }

    /**
     * @param field     : 高亮字段 也是 match要查询的字段
     * @param preTags   : 高亮前缀
     * @param postTags  : 高亮后缀
     * @param text      : 查询的值(会分词)
     * @param classType :  返回的list里的对象并且通过对象里面@Document注解indexName属性获取查询哪个索引
     * @return java.util.List<T>
     * @explain :  给查询到的值进行高亮
     * @Author Mhh
     * @Date 2021/12/21 15:15
     */
    @PostMapping("highlightBuilder")
    public <T> List<T> highlightBuilder(String field, String preTags, String postTags, String text, Class<T> classType) {
        return elasticsearchSelectService.highlightBuilder(field, preTags, postTags, text, classType);
    }


}
